'use strict';
const PACKET_TIME_DELAY = 600;
const BITS_IN_BYTE = 8;
const MACRO_BITS_SHIFT = (x) => (((x & 0xf0) >>> 4) + (x & 0x0f));// hi + lo
const MACRO_GROUP_METHOD = (x) => (x % 10);// lo
const MAC_SHIFT_INDEX = 0;
const TIME_SHIFT_INDEX = 1;
const MIX_ORDER_INDEX = 2;
const NOR_VALUE = (X) => (X.readUInt8(3) + X.readUInt8(4) + X.readUInt8(5));

const BLE_MAC_LEN = 6;
const TIME_LEN = 4;
const GROUP_LEN = 10;
const GROUP_TYPE_SIZE = 10;

const BIT_SWAP_DES_POS = 3;
// const F1 = [
//   222, 57, 147, 183, 102, 247, 149, 51, 164, 99, 31, 116, 138, 251, 192, 151, 76, 108, 129, 83, 92, 181, 120, 54, 153, 154, 49, 196, 246, 209, 87, 69, 79, 226, 201, 33, 119, 37, 7, 171, 26, 173, 131, 197, 95, 93, 48, 136, 85, 166, 53, 239, 186, 225, 38, 50, 223, 140, 158, 184, 231, 25, 224, 72, 21, 80, 172, 156, 47, 254, 236, 66, 78, 180, 210, 178, 213, 215, 134, 135, 199, 234, 200, 142, 141, 74, 214, 29, 244, 27, 144, 118, 130, 218, 139, 20, 52, 94, 143, 227, 96, 243, 73, 229, 170, 237, 204, 35, 241, 220, 235, 127, 101, 64, 46, 177, 163, 115, 216, 124, 8, 190, 14, 202, 91, 128, 253, 146, 105, 221, 174, 44, 182, 39, 43, 36, 233, 62, 24, 12, 179, 137, 238, 198, 132, 6, 133, 77, 157, 175, 104, 187, 65, 18, 98, 106, 123, 159, 114, 15, 145, 75, 245, 16, 126, 71, 205, 84, 121, 167, 55, 125, 248, 42, 56, 22, 230, 168, 10, 112, 195, 70, 32, 59, 162, 188, 63, 255, 203, 88, 150, 232, 242, 86, 252, 191, 9, 19, 111, 3, 169, 117, 148, 41, 110, 208, 155, 107, 30, 17, 206, 165, 40, 58, 100, 211, 68, 2, 228, 81, 13, 97, 113, 212, 122, 189, 103, 61, 160, 194, 240, 90, 217, 109, 1, 82, 176, 60, 89, 67, 5, 185, 4, 219, 250, 28, 207, 11, 45, 34, 249, 152, 193, 0, 23, 161
// ];
// const F1Order = [
//   253, 234, 217, 199, 242, 240, 145, 38, 120, 196, 178, 247, 139, 220, 122, 159, 163, 209, 153, 197, 95, 64, 175, 254, 138, 61, 40, 89, 245, 87, 208, 10, 182, 35, 249, 107, 135, 37, 54, 133, 212, 203, 173, 134, 131, 248, 114, 68, 46, 26, 55, 7, 96, 50, 23, 170, 174, 1, 213, 183, 237, 227, 137, 186, 113, 152, 71, 239, 216, 31, 181, 165, 63, 102, 85, 161, 16, 147, 72, 32, 65, 219, 235, 19, 167, 48, 193, 30, 189, 238, 231, 124, 20, 45, 97, 44, 100, 221, 154, 9, 214, 112, 4, 226, 150, 128, 155, 207, 17, 233, 204, 198, 179, 222, 158, 117, 11, 201, 91, 36, 22, 168, 224, 156, 119, 171, 164, 111, 125, 18, 92, 42, 144, 146, 78, 79, 47, 141, 12, 94, 57, 84, 83, 98, 90, 160, 127, 2, 202, 6, 190, 15, 251, 24, 25, 206, 67, 148, 58, 157, 228, 255, 184, 116, 8, 211, 49, 169, 177, 200, 104, 39, 66, 41, 130, 149, 236, 115, 75, 140, 73, 21, 132, 3, 59, 241, 52, 151, 185, 225, 121, 195, 14, 252, 229, 180, 27, 43, 143, 80, 82, 34, 123, 188, 106, 166, 210, 246, 205, 29, 74, 215, 223, 76, 86, 77, 118, 232, 93, 243, 109, 129, 0, 56, 62, 53, 33, 99, 218, 103, 176, 60, 191, 136, 81, 110, 70, 105, 142, 51, 230, 108, 192, 101, 88, 162, 28, 5, 172, 250, 244, 13, 194, 126, 69, 187
// ];
// const F2 = [
//   222, 57, 147, 183, 102, 247, 149, 51, 164, 99, 31, 116, 138, 251, 192, 151, 76, 108, 129, 83, 92, 181, 120, 54, 153, 154, 49, 196, 246, 209, 87, 69, 79, 226, 201, 33, 119, 37, 7, 171, 26, 173, 131, 197, 95, 93, 48, 136, 85, 166, 53, 239, 186, 225, 38, 50, 223, 140, 158, 184, 231, 25, 224, 72, 21, 80, 172, 156, 47, 254, 236, 66, 78, 180, 210, 178, 213, 215, 134, 135, 199, 234, 200, 142, 141, 74, 214, 29, 244, 27, 144, 118, 130, 218, 139, 20, 52, 94, 143, 227, 96, 243, 73, 229, 170, 237, 204, 35, 241, 220, 235, 127, 101, 64, 46, 177, 163, 115, 216, 124, 8, 190, 14, 202, 91, 128, 253, 146, 105, 221, 174, 44, 182, 39, 43, 36, 233, 62, 24, 12, 179, 137, 238, 198, 132, 6, 133, 77, 157, 175, 104, 187, 65, 18, 98, 106, 123, 159, 114, 15, 145, 75, 245, 16, 126, 71, 205, 84, 121, 167, 55, 125, 248, 42, 56, 22, 230, 168, 10, 112, 195, 70, 32, 59, 162, 188, 63, 255, 203, 88, 150, 232, 242, 86, 252, 191, 9, 19, 111, 3, 169, 117, 148, 41, 110, 208, 155, 107, 30, 17, 206, 165, 40, 58, 100, 211, 68, 2, 228, 81, 13, 97, 113, 212, 122, 189, 103, 61, 160, 194, 240, 90, 217, 109, 1, 82, 176, 60, 89, 67, 5, 185, 4, 219, 250, 28, 207, 11, 45, 34, 249, 152, 193, 0, 23, 161
// ];
// const F2Order = [
//   253, 234, 217, 199, 242, 240, 145, 38, 120, 196, 178, 247, 139, 220, 122, 159, 163, 209, 153, 197, 95, 64, 175, 254, 138, 61, 40, 89, 245, 87, 208, 10, 182, 35, 249, 107, 135, 37, 54, 133, 212, 203, 173, 134, 131, 248, 114, 68, 46, 26, 55, 7, 96, 50, 23, 170, 174, 1, 213, 183, 237, 227, 137, 186, 113, 152, 71, 239, 216, 31, 181, 165, 63, 102, 85, 161, 16, 147, 72, 32, 65, 219, 235, 19, 167, 48, 193, 30, 189, 238, 231, 124, 20, 45, 97, 44, 100, 221, 154, 9, 214, 112, 4, 226, 150, 128, 155, 207, 17, 233, 204, 198, 179, 222, 158, 117, 11, 201, 91, 36, 22, 168, 224, 156, 119, 171, 164, 111, 125, 18, 92, 42, 144, 146, 78, 79, 47, 141, 12, 94, 57, 84, 83, 98, 90, 160, 127, 2, 202, 6, 190, 15, 251, 24, 25, 206, 67, 148, 58, 157, 228, 255, 184, 116, 8, 211, 49, 169, 177, 200, 104, 39, 66, 41, 130, 149, 236, 115, 75, 140, 73, 21, 132, 3, 59, 241, 52, 151, 185, 225, 121, 195, 14, 252, 229, 180, 27, 43, 143, 80, 82, 34, 123, 188, 106, 166, 210, 246, 205, 29, 74, 215, 223, 76, 86, 77, 118, 232, 93, 243, 109, 129, 0, 56, 62, 53, 33, 99, 218, 103, 176, 60, 191, 136, 81, 110, 70, 105, 142, 51, 230, 108, 192, 101, 88, 162, 28, 5, 172, 250, 244, 13, 194, 126, 69, 187
// ];
// const Group = [
//   [ 0, 9, 3, 2, 4, 1, 6, 8, 5, 7 ],
//   [ 5, 4, 0, 8, 9, 2, 6, 1, 3, 7 ],
//   [ 3, 1, 7, 4, 5, 9, 6, 2, 0, 8 ],
//   [ 0, 5, 3, 9, 4, 8, 6, 2, 1, 7 ],
//   [ 6, 3, 0, 1, 9, 4, 7, 8, 2, 5 ],
//   [ 1, 7, 0, 8, 9, 2, 3, 5, 6, 4 ],
//   [ 9, 7, 0, 8, 1, 6, 3, 2, 4, 5 ],
//   [ 3, 2, 5, 7, 4, 6, 0, 8, 1, 9 ],
//   [ 9, 0, 8, 7, 5, 1, 3, 2, 4, 6 ],
//   [ 6, 7, 8, 9, 3, 5, 2, 1, 4, 0 ]
// ];
const F1 = [
  10, 246, 119, 21, 9, 49, 96, 204, 208,
  93, 155, 30, 18, 48, 151, 14, 215, 98,
  185, 187, 180, 82, 136, 231, 206, 123,
  27, 46, 227, 139, 170, 184, 62, 25, 236,
  89, 144, 226, 80, 67, 244, 58, 138, 159,
  255, 0, 130, 205, 104, 79, 224, 15, 210,
  199, 157, 166, 219, 122, 91, 125, 147,
  209, 234, 220, 183, 253, 213, 197, 66,
  64, 172, 33, 36, 100, 200, 225, 181,
  13, 250, 5, 202, 217, 245, 99, 4, 249,
  178, 70, 190, 107, 24, 32, 74, 182, 164,
  153, 16, 77, 115, 173, 237, 149, 50, 69,
  160, 108, 193, 112, 6, 84, 232, 78, 54,
  86, 90, 83, 167, 243, 168, 51, 105, 45,
  55, 85, 169, 140, 142, 235, 222, 1, 23,
  109, 196, 61, 207, 124, 19, 150, 31, 233,
  254, 201, 101, 189, 238, 59, 106, 75, 148,
  141, 26, 132, 103, 40, 17, 72, 211, 117,
  179, 203, 68, 194, 39, 137, 95, 87, 191,
  162, 37, 135, 47, 120, 52, 41, 192, 198,
  63, 118, 221, 43, 71, 239, 241, 3, 177, 38,
  230, 176, 20, 116, 126, 131, 188, 195, 154,
  94, 214, 22, 28, 143, 247, 223, 171, 111,
  158, 175, 81, 165, 163, 134, 133, 128, 34,
  252, 146, 129, 57, 152, 7, 121, 65, 8, 110,
  127, 92, 251, 12, 248, 114, 97, 113, 174,
  11, 42, 218, 60, 242, 76, 212, 156, 53,
  102, 229, 44, 29, 145, 35, 228, 216, 2,
  161, 240, 73, 88, 56, 186
];
const F1Order = [
  45, 129, 249, 183, 84, 79, 108, 218, 221, 4, 0, 232,
  226, 77, 15, 51, 96, 154, 12, 136, 188, 3, 197,
  130, 90, 33, 150, 26, 198, 244, 11, 138, 91,
  71, 212, 246, 72, 168, 185, 162, 153, 173, 233,
  179, 243, 121, 27, 170, 13, 5, 102, 119, 172,
  240, 112, 122, 254, 216, 41, 145, 235, 133, 32,
  176, 69, 220, 68, 39, 160, 103, 87, 180, 155, 252,
  92, 147, 237, 97, 111, 49, 38, 206, 21, 115, 109,
  123, 113, 165, 253, 35, 114, 58, 224, 9, 195, 164,
  6, 229, 17, 83, 73, 142, 241, 152, 48, 120, 146,
  89, 105, 131, 222, 203, 107, 230, 228, 98, 189, 157,
  177, 2, 171, 219, 57, 25, 135, 59, 190, 223, 211,
  215, 46, 191, 151, 210, 209, 169, 22, 163, 42, 29,
  125, 149, 126, 199, 36, 245, 214, 60, 148, 101, 137,
  14, 217, 95, 194, 10, 239, 54, 204, 43, 104, 250, 167,
  208, 94, 207, 55, 116, 118, 124, 30, 202, 70, 99, 231,
  205, 187, 184, 86, 158, 20, 76, 93, 64, 31, 18, 255,
  19, 192, 143, 88, 166, 174, 106, 161, 193, 132, 67,
  175, 53, 74, 141, 80, 159, 7, 47, 24, 134, 8, 61, 52,
  156, 238, 66, 196, 16, 248, 81, 234, 56, 63, 178, 128,
  201, 50, 75, 37, 28, 247, 242, 186, 23, 110, 139, 62,
  127, 34, 100, 144, 181, 251, 182, 236, 117, 40, 82, 1,
  200, 227, 85, 78, 225, 213, 65, 140, 44
];
const F2 = [
  216, 20, 186, 234, 99, 137, 84, 106, 54, 93, 53, 245,
  79, 35, 241, 217, 149, 183, 65, 1, 24, 119, 96, 200,
  155, 176, 180, 11, 80, 87, 177, 130, 226, 250, 251,
  184, 202, 121, 233, 147, 211, 203, 229, 159, 97, 248,
  145, 218, 52, 49, 13, 48, 16, 68, 26, 113, 83, 169,
  165, 231, 242, 235, 230, 188, 151, 225, 164, 227, 63,
  75, 161, 77, 182, 221, 194, 212, 74, 134, 6, 30, 112,
  7, 14, 62, 213, 246, 118, 124, 72, 61, 135, 157, 47,
  236, 139, 156, 78, 116, 190, 133, 143, 64, 255, 31,
  140, 108, 252, 239, 17, 172, 2, 144, 28, 51, 85, 82,
  148, 21, 92, 18, 27, 45, 201, 174, 111, 240, 254, 66,
  195, 81, 189, 168, 95, 5, 104, 44, 69, 58, 36, 88,
  109, 243, 0, 8, 60, 153, 193, 76, 114, 158, 253, 167,
  196, 38, 23, 91, 125, 131, 237, 171, 40, 210, 90, 43,
  209, 33, 175, 160, 126, 39, 223, 120, 89, 152, 141,
  70, 219, 170, 9, 71, 22, 228, 127, 238, 25, 102, 129,
  50, 197, 115, 192, 73, 215, 123, 181, 98, 12, 142,
  163, 208, 178, 128, 42, 29, 107, 249, 103, 247, 55,
  110, 122, 162, 220, 56, 198, 57, 132, 59, 4, 199, 205,
  185, 154, 222, 224, 117, 86, 232, 191, 138, 32, 41,
  173, 19, 204, 187, 10, 15, 37, 207, 34, 150, 100, 206,
  101, 46, 67, 94, 136, 179, 244, 214, 146, 3, 166, 105
];
const F2Order = [
  142, 19, 110, 253, 218, 133, 78, 81, 143, 178, 236,
  27, 196, 50, 82, 237, 52, 108, 119, 233, 1, 117,
  180, 154, 20, 184, 54, 120, 112, 203, 79, 103, 230,
  165, 240, 13, 138, 238, 153, 169, 160, 231, 202, 163,
  135, 121, 245, 92, 51, 49, 187, 113, 48, 10, 8, 208,
  213, 215, 137, 217, 144, 89, 83, 68, 101, 18, 127, 246,
  53, 136, 175, 179, 88, 191, 76, 69, 147, 71, 96, 12,
  28, 129, 115, 56, 6, 114, 226, 29, 139, 172, 162, 155,
  118, 9, 247, 132, 22, 44, 195, 4, 242, 244, 185, 206,
  134, 255, 7, 204, 105, 140, 209, 124, 80, 55, 148, 189,
  97, 225, 86, 21, 171, 37, 210, 193, 87, 156, 168, 182,
  201, 186, 31, 157, 216, 99, 77, 90, 248, 5, 229, 94, 104,
  174, 197, 100, 111, 46, 252, 39, 116, 16, 241, 64, 173,
  145, 222, 24, 95, 91, 149, 43, 167, 70, 211, 198, 66, 58,
  254, 151, 131, 57, 177, 159, 109, 232, 123, 166, 25, 30,
  200, 249, 26, 194, 72, 17, 35, 221, 2, 235, 63, 130, 98,
  228, 190, 146, 74, 128, 152, 188, 214, 219, 23, 122, 36,
  41, 234, 220, 243, 239, 199, 164, 161, 40, 75, 84, 251,
  192, 0, 15, 47, 176, 212, 73, 223, 170, 224, 65, 32, 67,
  181, 42, 62, 59, 227, 38, 3, 61, 93, 158, 183, 107, 125,
  14, 60, 141, 250, 11, 85, 207, 45, 205, 33, 34, 106, 150,
  126, 102
];
const Group = [
  [ 6, 7, 4, 0, 9, 5, 8, 2, 1, 3 ],
  [ 9, 8, 3, 5, 7, 6, 2, 0, 4, 1 ],
  [ 6, 5, 2, 1, 4, 0, 9, 3, 8, 7 ],
  [ 5, 4, 7, 3, 2, 6, 1, 9, 0, 8 ],
  [ 4, 6, 7, 3, 2, 0, 9, 8, 5, 1 ],
  [ 3, 0, 8, 5, 6, 4, 7, 1, 2, 9 ],
  [ 7, 9, 4, 5, 8, 3, 2, 6, 1, 0 ],
  [ 0, 4, 7, 3, 8, 5, 9, 6, 1, 2 ],
  [ 0, 2, 4, 6, 5, 7, 1, 8, 9, 3 ],
  [ 9, 3, 4, 7, 2, 8, 1, 6, 5, 0 ]
];
function byteSwapBit(buf, srcOffset, desOffset, srcPos, desPos = BIT_SWAP_DES_POS) {
  let src = buf.readUInt8(srcOffset),
    des = buf.readUInt8(desOffset);
  const desTemp = (des & (1 << desPos)) >>> desPos;
  des = (des & (~(1 << desPos))) | (((src & (1 << srcPos)) >>> srcPos) << desPos);
  src = (src & (~(1 << srcPos))) | (desTemp << srcPos);
  buf.writeUInt8(src, srcOffset);
  buf.writeUInt8(des, desOffset);
}
function mapDatas(buf) {
  for (let i = 0; i < buf.length; i++) {
    const bindex = buf.readUInt8(i);
    buf.writeUInt8(i < BLE_MAC_LEN ? F1[bindex] : F2[bindex], i);
  }
}
function remapDatas(buf, desLockcodeBuf, desTimeBuf) {
  for (let i = 0; i < buf.length; i++) {
    const bindex = (i < BLE_MAC_LEN ? desLockcodeBuf.readUInt8(i) : desTimeBuf.readUInt8(i - BLE_MAC_LEN));
    buf.writeUInt8(i < BLE_MAC_LEN ? F1Order[bindex] : F2Order[bindex], i);
  }
}
function rightShift(buf, offset, length, move) {
  const moveBytes = Math.floor(move / BITS_IN_BYTE) % length;
  const moveBits = move % BITS_IN_BYTE;
  // move bytes
  const bufTemp = Buffer.from(buf);
  for (let i = offset + length - moveBytes, j = offset; j < offset + length; j++, i++) {
    if (i >= offset + length) i = offset;
    buf.writeUInt8(bufTemp.readUInt8(i), j);
  }
  // move bits
  const mask = 2 ** moveBits;
  let res = buf.readUInt8(offset + length - 1) % mask;
  let temp;
  for (let i = offset; i < offset + length; i++) {
    temp = buf.readUInt8(i);
    temp = (temp >>> moveBits);
    temp |= (res << (BITS_IN_BYTE - moveBits));
    res = buf.readUInt8(i) % mask;
    buf.writeUInt8(temp, i, true);
  }
}
function leftShift(buf, offset, length, move) {
  const moveBytes = Math.floor(move / BITS_IN_BYTE) % length;
  const moveBits = move % BITS_IN_BYTE;
  // move bytes
  const bufTemp = Buffer.from(buf);
  for (let i = offset + moveBytes, j = offset; j < offset + length; j++, i++) {
    if (i >= offset + length) i = offset;
    buf.writeUInt8(bufTemp.readUInt8(i), j);
  }
  // move bits
  const mask = 2 ** (BITS_IN_BYTE - moveBits);
  let res = buf.readUInt8(offset) / mask;
  let temp;
  for (let i = offset + length - 1; i >= offset; i--) {
    temp = buf.readUInt8(i);
    temp = (temp << moveBits);
    temp |= res;
    res = Math.floor(buf.readUInt8(i) / mask);
    buf.writeUInt8(temp, i, true);
  }
}
function setGroup(buf, group) {
  const bufTemp = Buffer.from(buf);
  for (let i = 0; i < buf.length; i++) {
    buf.writeUInt8(bufTemp.readUInt8(group[i]), i);
  }
}
function recoverGroup(buf, group) {
  const bufTemp = Buffer.from(buf);
  for (let i = 0; i < buf.length; i++) {
    buf.writeUInt8(bufTemp.readUInt8(i), group[i]);
  }
}
function genToken(lockcode, expirein) {
  const lockcodeBuf = Buffer.from(lockcode, 'hex');
  const srcBuf = Buffer.alloc(10);
  srcBuf.write(lockcode, 0, 6, 'hex');
  srcBuf.writeUInt32LE(expirein, 6);
  // swap data bits,
  // expire first byte 0~5 bits swap lockcode 0~5 bytes 3th bit,
  // expire 2th byte 6~7 bits swap lockcode 8~9 bytes 3th bit,
  byteSwapBit(srcBuf, 6, 0, 0, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 6, 1, 1, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 6, 2, 2, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 6, 3, 3, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 6, 4, 4, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 6, 5, 5, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 7, 8, 6, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 7, 9, 7, BIT_SWAP_DES_POS);
  // map byte data
  mapDatas(srcBuf);
  // shift
  rightShift(srcBuf, 0, BLE_MAC_LEN, MACRO_BITS_SHIFT(lockcodeBuf.readUInt8(MAC_SHIFT_INDEX)));
  rightShift(srcBuf, 6, TIME_LEN, MACRO_BITS_SHIFT(lockcodeBuf.readUInt8(TIME_SHIFT_INDEX)));
  // Mix
  const desBuf = Buffer.alloc(10);
  desBuf.writeUInt8(srcBuf.readUInt8(0), 0);
  desBuf.writeUInt8(srcBuf.readUInt8(6), 1);
  desBuf.writeUInt8(srcBuf.readUInt8(1), 2);
  desBuf.writeUInt8(srcBuf.readUInt8(7), 3);
  desBuf.writeUInt8(srcBuf.readUInt8(2), 4);
  desBuf.writeUInt8(srcBuf.readUInt8(8), 5);
  desBuf.writeUInt8(srcBuf.readUInt8(3), 6);
  desBuf.writeUInt8(srcBuf.readUInt8(9), 7);
  desBuf.writeUInt8(srcBuf.readUInt8(4), 8);
  desBuf.writeUInt8(srcBuf.readUInt8(5), 9);
  // set group
  setGroup(desBuf, Group[MACRO_GROUP_METHOD(lockcodeBuf.readUInt8(MIX_ORDER_INDEX))]);
  // nor
  for (let i = 0; i < desBuf.length; i++) {
    let temp = desBuf.readUInt8(i);
    temp ^= NOR_VALUE(lockcodeBuf);
    desBuf.writeUInt8(temp, i, true);
  }
  // copy tokens
  return desBuf;
}
function validToken(token, lockcode, time) {
  const desLockcodeBuf = Buffer.alloc(BLE_MAC_LEN);
  const desTimeBuf = Buffer.alloc(TIME_LEN);
  const srcBuf = Buffer.from(token, 'hex');
  const lockcodeBuf = Buffer.from(lockcode, 'hex');
  const timeBuf = Buffer.alloc(TIME_LEN);
  timeBuf.writeUInt32LE(time, 0);
  // nor
  for (let i = 0; i < srcBuf.length; i++) {
    let temp = srcBuf.readUInt8(i);
    temp ^= NOR_VALUE(lockcodeBuf);
    srcBuf.writeUInt8(temp, i, true);
  }
  // set group
  recoverGroup(srcBuf, Group[MACRO_GROUP_METHOD(lockcodeBuf.readUInt8(MIX_ORDER_INDEX))]);
  // Mix
  desLockcodeBuf.writeUInt8(srcBuf.readUInt8(0), 0);
  desTimeBuf.writeUInt8(srcBuf.readUInt8(1), 0);
  desLockcodeBuf.writeUInt8(srcBuf.readUInt8(2), 1);
  desTimeBuf.writeUInt8(srcBuf.readUInt8(3), 1);
  desLockcodeBuf.writeUInt8(srcBuf.readUInt8(4), 2);
  desTimeBuf.writeUInt8(srcBuf.readUInt8(5), 2);
  desLockcodeBuf.writeUInt8(srcBuf.readUInt8(6), 3);
  desTimeBuf.writeUInt8(srcBuf.readUInt8(7), 3);
  desLockcodeBuf.writeUInt8(srcBuf.readUInt8(8), 4);
  desLockcodeBuf.writeUInt8(srcBuf.readUInt8(9), 5);
  // shift
  leftShift(desLockcodeBuf, 0, BLE_MAC_LEN, MACRO_BITS_SHIFT(lockcodeBuf.readUInt8(MAC_SHIFT_INDEX)));
  leftShift(desTimeBuf, 0, TIME_LEN, MACRO_BITS_SHIFT(lockcodeBuf.readUInt8(TIME_SHIFT_INDEX)));
  // map byte data
  remapDatas(srcBuf, desLockcodeBuf, desTimeBuf);
  // swap bytes and bits
  byteSwapBit(srcBuf, 6, 0, 0, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 6, 1, 1, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 6, 2, 2, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 6, 3, 3, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 6, 4, 4, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 6, 5, 5, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 7, 8, 6, BIT_SWAP_DES_POS);
  byteSwapBit(srcBuf, 7, 9, 7, BIT_SWAP_DES_POS);
  const desLockcode = srcBuf.toString('hex', 0, BLE_MAC_LEN).toUpperCase();
  if (desLockcode !== lockcode) {
    throw Error(`valid error: lockcode[${desLockcode}] is not same to ${lockcode}`);
  }
  const desTime = srcBuf.readUInt32LE(6);
  if (desTime < time) {
    //throw Error(`valid error: time[${desTime}] <  ${time}`);
  }
}
function crcUint8(buf) {
  let len = buf.length,
    j = 0,
    crc = 0;
  while (len-- !== 0) {
    for (let i = 0x80; i !== 0; i >>>= 1) {
      if ((crc & 0x80) !== 0) {
        crc <<= 1;
        crc ^= 0x07;
      } else {
        crc <<= 1;
      }
      if ((buf.readUInt8(j) & i) !== 0) {
        crc ^= 0x07;
      }
    }
    j++;
  }
  return (crc & 0xFF) >>> 0;
}
function encrypt(buf, lockcode) {
  const lockcodebuf = Buffer.from(lockcode, 'hex');
  const lowBit4 = lockcodebuf.readUInt8(lockcodebuf.length - 1) & 0x0F;
  const highBit4 = lockcodebuf.readUInt8(0) >>> 4;
  let temp = 0x00;
  for (let i = 0; i < buf.length; i++) {
    temp = buf.readUInt8(i);
    buf.writeUInt8(((temp ^ lowBit4) & 0x0F) | (temp & 0xF0), i);
  }
  if (!highBit4) return;
  const tempBuf = Buffer.from(buf);
  for (let i = 0, j = highBit4; i < buf.length; i++, j++) {
    if (j === buf.length) j = 0;
    buf.writeUInt8(tempBuf.readUInt8(i), j);
  }
}
function decrypt(buf, lockcode) {
  const lockcodebuf = Buffer.from(lockcode, 'hex');
  const lowBit4 = lockcodebuf.readUInt8(lockcodebuf.length - 1) & 0x0F;
  const highBit4 = lockcodebuf.readUInt8(0) >>> 4;
  let temp = 0x00;
  if (highBit4) {
    const tempBuf = Buffer.from(buf);
    for (let i = 0, j = highBit4; i < buf.length; i++, j++) {
      if (j === buf.length) j = 0;
      buf.writeUInt8(tempBuf.readUInt8(j), i);
    }
  }
  for (let i = 0; i < buf.length; i++) {
    temp = buf.readUInt8(i);
    buf.writeUInt8(((temp ^ lowBit4) & 0x0F) | (temp & 0xF0), i);
  }
}

function lockDecode(lockId, hexstr) {
    const result = {
        result: 0
    };
    const buf = Buffer.from(hexstr, 'hex');
    decrypt(buf, lockId);
    const nbuf = Buffer.alloc(buf.length - 1);
    buf.copy(nbuf, 0, 0, buf.length - 1);
    const crc = crcUint8(nbuf);
    if (crc !== buf.readUInt8(buf.length - 1)) {
        throw Error(`crc error: local[${crc}]-remote[${buf.readUInt8(buf.length - 1)}]`);
    }
    const tokenBuf = Buffer.alloc(10).fill(0x00);
    buf.copy(tokenBuf, 0, 0, 10);
    validToken(tokenBuf, lockId, Math.floor(Date.now() / 1000));
    result.cmd = buf.readUInt8(10);
    result.result = buf.readUInt8(11);
    if (result.cmd === 6) {
      result.data = {
        type: buf.readUInt8(14),
        time: buf.readUInt32BE(15)
      };
    } else {
      result.data = buf.slice(12, buf.length - 1);
    }
    return result;
}
function lockEncode(lockId, buf) {
    const tokenBuf = genToken(lockId, Math.floor(Date.now() / 1000) + PACKET_TIME_DELAY);
    tokenBuf.copy(buf);
    const nbuf = Buffer.alloc(buf.length + 1).fill(0x00);
    buf.copy(nbuf);
    nbuf.writeUInt8(crcUint8(buf), nbuf.length - 1);
    encrypt(nbuf, lockId);
    return nbuf;
}
module.exports = {
    lockEncode,
    lockDecode
};
(function() {
  // const lockcode = '50338B1F11FF';
  // const time = 1517293566; // Math.ceil(Date.now() / 1000) + 600;
  // console.log(time);
  // const token = genToken(lockcode, time);
  // console.log(token.toString('hex'));
  // validToken(token.toString('hex'), lockcode, time);
  // const dataBuf = Buffer.concat([token, token], 20);
  // console.log(dataBuf.toString('hex'));
  // encrypt(dataBuf, lockcode);
  // console.log(dataBuf.toString('hex'));
  // decrypt(dataBuf, lockcode);
  // console.log(dataBuf.toString('hex'));
  const res = lockDecode('50338BFF8149', '090909093D73E9BF2A9014B48DAEE60908080809');
  console.log(res);
})();